#include "myi2c.h"

/*
  I2C协议的开漏输出给1就是默认1分不清是谁说的， 开漏输出下拉给0才能确认是从机主动给的。

*/
static inline void myi2c_delayus(uint32_t us)
{
  volatile uint32_t freq = 0, count = 0;
  freq  = HAL_RCC_GetSysClockFreq() / 1e6;
  count = us * freq;
  // small busy-wait to approximate ~2-5us depending on clock/optimization
  for (volatile int i = 0; i < count; ++i) {
    __NOP();
  }
}

void myi2c_w_scl(uint8_t Bit)
{
  HAL_GPIO_WritePin(I2C_PORT, I2C_SCL_PIN, Bit ? GPIO_PIN_SET : GPIO_PIN_RESET);
  myi2c_delayus(10);
}

void myi2c_w_sda(uint8_t Bit)
{
  HAL_GPIO_WritePin(I2C_PORT, I2C_SDA_PIN, Bit ? GPIO_PIN_SET : GPIO_PIN_RESET);
  myi2c_delayus(10);
}

uint8_t myi2c_r_sda(void)
{
  uint8_t BitValue;
  BitValue = (uint8_t)HAL_GPIO_ReadPin(I2C_PORT, I2C_SDA_PIN);
  myi2c_delayus(10);
  return BitValue;
}

void myi2c_init(void)
{
  // __HAL_RCC_GPIOB_CLK_ENABLE();

  // GPIO_InitTypeDef GPIO_Initstruct = {0};
  // GPIO_Initstruct.Pin              = I2C_SCL_PIN | GPIO_PIN_11; // PB10=SCL, PB11=SDA
  // GPIO_Initstruct.Mode             = GPIO_MODE_OUTPUT_OD;       // Open-drain for I2C-style lines
  // GPIO_Initstruct.Pull             = GPIO_PULLUP;               // Use pull-ups (internal or external)
  // GPIO_Initstruct.Speed            = GPIO_SPEED_FREQ_VERY_HIGH;
  // HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);

  // HAL_GPIO_WritePin(GPIOB, I2C_SCL_PIN | GPIO_PIN_11, GPIO_PIN_SET); // Idle high

  return;
}

void myi2c_start(void)
{
  myi2c_w_sda(1);
  myi2c_w_scl(1);
  myi2c_w_sda(0);
  myi2c_w_scl(0);
}

void myi2c_stop(void)
{
  myi2c_w_sda(0);
  myi2c_w_scl(1);
  myi2c_w_sda(1);
}

void myi2c_sendbyte(uint8_t Byte)
{
  // 传入非0数据就会写1，不管是0x80, 0x40, 0x20.
  uint8_t i;
  for (i = 0; i < 8; i++) {
    myi2c_w_sda(Byte & (0x80 >> i));
    myi2c_w_scl(1);
    myi2c_w_scl(0);
  }
}

uint8_t myi2c_receivebyte(void)
{
  uint8_t Byte = 0x00, i;
  myi2c_w_sda(1); // 释放数据线给从机放数据
  for (i = 0; i < 8; i++) {
    myi2c_w_scl(1); // 释放时钟线准备读数据
    if (myi2c_r_sda() == 1) {
      Byte |= (0x80 >> i); // 判断1就赋1，不是就保持默认0.
    }
    myi2c_w_scl(0);
  }
  return Byte;
}
// 发送应答为0， 不应答为1.
void myi2c_sendack(uint8_t AckBit)
{
  myi2c_w_sda(AckBit);
  myi2c_w_scl(1);
  myi2c_w_scl(0);
}

// 正确接收则返回0，不正确则为1.
uint8_t myi2c_receiveack(void)
{
  uint8_t AckBit;
  myi2c_w_sda(1); // 释放数据线给从机放数据
  myi2c_w_scl(1); // 释放时钟线准备读数据
  AckBit = myi2c_r_sda();
  myi2c_w_scl(0); // 进入下一个时钟单元
  return AckBit;
}